home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / eev100r1.zip / POSTFIX.PAS < prev    next >
Pascal/Delphi Source File  |  1992-01-03  |  16KB  |  710 lines

  1. Unit PostFix;
  2.  
  3. { ------------------------------------------------------------------------
  4.   POSTFIX.PAS
  5.   ------------------------------------------------------------------------
  6.  
  7.   Version 1.00, Revision 0, December 28, 1991
  8.  
  9.   Written by: David J. Firth
  10.               5665-A2 Parkville St.
  11.               Columbus, OH 43229
  12.  
  13.   This unit provides a complete reverse polish notation (RPN) expression
  14.   evaluator.  Each part of the RPN expression needs to be separated by a
  15.   space.  The evaluator supports the following functions:
  16.  
  17.   + - * / PI ABS ARCTAN COS EXP LN SQR SQRT
  18.  
  19.   The RPN evaluator does not have its own tokenizer.  Instead, since
  20.   the expression tokens must be separated by spaces, Turbo's own
  21.   ParamStr tokenizer can be fooled into doing the job.  Due to the
  22.   limitations imposed by DOS on the size of the command tail, the
  23.   length of the string to evaluate will be truncated at 120 characters.
  24.   My thanks to PC Techniques magazine for printing a HAX with this
  25.   suggestion in it.
  26.  
  27.   The evaluator package includes routines to read and write values
  28.   to and from variables.  Variables should be 20 or characters or
  29.   less in length.  During expression evaluation, any unrecognized
  30.   string of characters will be assumed to be a variable.
  31.  
  32.   Two procedures are provided for expression evaluation, Calculate and
  33.   CalcAndStore.  Calculate will evaluate the expression and return the
  34.   result to the caller.  CalcAndStore will evaluate the expression and
  35.   store the result in a variable.
  36.  
  37.   POSTFIX.PAS has two major data structures allocated on the heap.
  38.   The first is a stack, used for the processing of RPN expressions.
  39.   The other is a linked list used to store variables.  Before the
  40.   application program is ended, the procedure DestroyList should
  41.   be called to deallocate the memory taken by these structures.
  42.  
  43.   ------------------------------------------------------------------------ }
  44.  
  45. Interface
  46.  
  47. type
  48.  
  49.   Str20 = string[20];                 {store variable IDs this way to conserve}
  50.   Str128 = string[128];
  51.  
  52.   VariablePtr = ^VariableType;        {for dynamic allocation of records }
  53.  
  54.   VariableType = record
  55.     ID    : Str20;                    {the id of the variable, with @s   }
  56.     Value : real;                     {the current value of the variable }
  57.     Next  : VariablePtr;              {hook to next record in linked list}
  58.   end; {VariableType}
  59.  
  60.   StackItemPtr = ^StackItemType;      {for dynamic allocation of records }
  61.  
  62.   StackItemType = record
  63.     Value : real;                     {the value to be "operated" upon   }
  64.     Next  : StackItemPtr;             {hook to next record in linked list}
  65.   end; {StackItemType}
  66.  
  67. var
  68.  
  69.   HPtr,                               {head of variable list       }
  70.   TPtr,                               {tail of variable list       }
  71.   SPtr  : VariablePtr;                {used to search variable list}
  72.  
  73.   STPtr : StackItemPtr;               {the top of the stack}
  74.  
  75. procedure StoreVariable(VariableID:str20;MyValue:real);
  76. procedure ReadVariable(VariableID:str20;var MyValue:real;var MyError:boolean);
  77. procedure DestroyList;
  78.  
  79. procedure Calculate(MyFormula:string;var MyResult:real;var MyError:boolean);
  80. procedure CalcAndStore(MyFormula:string;StoreID:str20;var MyError:boolean);
  81.  
  82. Implementation
  83.  
  84. Uses
  85.  
  86.   DFStr;
  87.  
  88. { ------------------------------------------------------------------------ }
  89.  
  90. function __ParamCount(MyStr:string):byte;
  91.  
  92. {this routine is a work-alike of Turbo's own ParamCount function. this
  93.  routine requires my DFStr unit to operate.}
  94.  
  95. var
  96.  
  97.   Count,
  98.   Index  : byte;
  99.  
  100. begin
  101.  
  102.   MyStr := __RemWhiteStr(MyStr,_Leading);
  103.   MyStr := __RemWhiteStr(MyStr,_Trailing);
  104.  
  105.   Count := 0;
  106.   for Index := 1 to length(MyStr) do
  107.     if MyStr[Index]=' ' then inc(Count);
  108.  
  109.   __ParamCount := Count+1;
  110.  
  111. end; {__ParamCount}
  112.  
  113. { ------------------------------------------------------------------------ }
  114.  
  115. function __ParamStr(Index:byte;MyStr:string):string;
  116.  
  117. var
  118.  
  119.   TempStr : string;
  120.   I,
  121.   J,
  122.   P,
  123.   Count   : byte;
  124.   Spaces  : array[0..256] of byte;
  125.  
  126. begin
  127.  
  128.   TempStr := '';
  129.  
  130.   fillchar(Spaces,sizeof(Spaces),0);
  131.  
  132.   Count := __ParamCount(MyStr);
  133.  
  134.   if (Index<=Count) AND (Index>0) then begin
  135.  
  136.     MyStr := __RemWhiteStr(MyStr,_Leading);
  137.     MyStr := __RemWhiteStr(MyStr,_Trailing);
  138.  
  139.     MyStr := ' ' + MyStr + ' ';
  140.  
  141.     {load Spaces}
  142.     J := 0;
  143.     for I := 1 to length(MyStr) do begin
  144.       if MyStr[I] = ' ' then begin
  145.         Spaces[J] := I;
  146.         inc(J);
  147.       end;
  148.     end; {for}
  149.  
  150.     {get the parameter}
  151.     TempStr := copy(MyStr,Spaces[Index-1]+1,Spaces[Index]-Spaces[Index-1]-1);
  152.  
  153.   end;
  154.  
  155.   __ParamStr := TempStr;
  156.  
  157. end; {__ParamStr}
  158.  
  159. { ------------------------------------------------------------------------ }
  160.  
  161. procedure Pop(var MyValue:real;var MyError:boolean);
  162.  
  163. var
  164.  
  165.   TempPtr : StackItemPtr;
  166.  
  167. begin
  168.  
  169.   if STPtr=nil then begin
  170.     {tried to pop empty stack -- error!}
  171.     MyValue := 0;
  172.     MyError := true;
  173.   end
  174.   else begin
  175.     {get value}
  176.     MyValue := STPtr^.Value;
  177.     MyError := false;
  178.     {dispose of the record at the top of the stack}
  179.     TempPtr := STPtr;
  180.     STPtr := STPtr^.Next;
  181.     dispose(TempPtr);
  182.   end; {if-else}
  183.  
  184. end; {Pop}
  185.  
  186. { ------------------------------------------------------------------------ }
  187.  
  188. procedure Push(MyValue:real);
  189.  
  190. var
  191.  
  192.   TempPtr : StackItemPtr;
  193.  
  194. begin
  195.  
  196.   {create record on heap for value}
  197.   new(TempPtr);
  198.   TempPtr^.Value := MyValue;
  199.  
  200.   {attach new record as top of stack}
  201.   TempPtr^.Next := STPtr;
  202.   STPtr := TempPtr;
  203.  
  204. end; {Push}
  205.  
  206. { ------------------------------------------------------------------------ }
  207.  
  208. procedure DestroyStack(MyPtr:StackItemPtr);
  209.  
  210. begin
  211.  
  212.   if MyPtr^.Next<>nil then
  213.     DestroyStack(MyPtr^.Next);
  214.  
  215.   dispose(MyPtr);
  216.  
  217. end; {DestroyStack}
  218.  
  219. { ------------------------------------------------------------------------ }
  220.  
  221. procedure GetPointerTo(VariableID:str20;var MPtr:VariablePtr);
  222.  
  223. var
  224.  
  225.   Done : boolean;
  226.   XPtr : VariablePtr;
  227.  
  228. begin
  229.  
  230.   MPtr := nil;
  231.   XPtr := HPtr;
  232.  
  233.   Done := false;
  234.   while (not Done) do begin
  235.  
  236.     if XPtr^.ID=VariableID then
  237.       MPtr := XPtr;
  238.  
  239.     if XPtr^.Next=nil then
  240.       Done := true
  241.     else
  242.       XPtr := XPtr^.Next;
  243.  
  244.   end; {while}
  245.  
  246. end; {GetPointerTo}
  247.  
  248. { ------------------------------------------------------------------------ }
  249.  
  250. procedure ReadVariable(VariableID:str20;var MyValue:real;var MyError:boolean);
  251.  
  252. var
  253.  
  254.   MPtr : VariablePtr;
  255.  
  256. begin
  257.  
  258.   MyError := false;
  259.   MyValue := 0;
  260.  
  261.   GetPointerTo(VariableID,MPtr);
  262.  
  263.   if MPtr<>nil then begin
  264.     MyValue := MPtr^.Value
  265.   end
  266.   else begin
  267.     MyError := true;
  268.   end;
  269.  
  270. end; {ReadVariable}
  271.  
  272. { ------------------------------------------------------------------------ }
  273.  
  274. procedure StoreVariable(VariableID:str20;MyValue:real);
  275.  
  276. var
  277.  
  278.   WorkingRec : VariableType;
  279.  
  280. begin
  281.  
  282.   fillchar(WorkingRec,sizeof(WorkingRec),0);
  283.   WorkingRec.ID := VariableID;
  284.   WorkingRec.Value := MyValue;
  285.  
  286.   If HPtr = nil then begin
  287.  
  288.     {this is the first record added to the list}
  289.  
  290.     New(HPtr);                                {allocate 1st record in LL }
  291.     TPtr := HPtr;                             {init tail (= head)        }
  292.     TPtr^ := WorkingRec;                      {add new record as head    }
  293.     TPtr^.Next := nil;                        {set the next link for tail}
  294.  
  295.   end
  296.   else begin
  297.  
  298.     GetPointerTo(VariableID,SPtr);
  299.  
  300.     if SPtr <> nil then begin
  301.  
  302.       {the list exists and so does the variable -- modify value}
  303.  
  304.       SPtr^.Value := MyValue;
  305.  
  306.     end
  307.     else begin
  308.  
  309.       {the list exists, but the variable doesn't -- add it}
  310.  
  311.       New(SPtr);                          {allocate new record for LL }
  312.       SPtr^ := WorkingRec;                {put info in new LL record  }
  313.       TPtr^.Next := SPtr;                 {add new record as tail     }
  314.       SPtr^.Next := nil;                  {set the new link for tail  }
  315.       TPtr := SPtr;                       {point tail to new record   }
  316.  
  317.     end; {if-else}
  318.  
  319.   end;
  320.  
  321. end; {StoreVariable}
  322.  
  323. { ------------------------------------------------------------------------- }
  324.  
  325. Procedure DestroyFieldList(TempPtr:VariablePtr);
  326.  
  327. { This procedure recursively destroys a linked list }
  328.  
  329. Begin
  330.  
  331.   If TempPtr^.Next <> nil then
  332.     DestroyFieldList(TempPtr^.Next);
  333.  
  334.   Dispose(TempPtr);
  335.  
  336. End;
  337.  
  338. { ------------------------------------------------------------------------ }
  339.  
  340. procedure DestroyList;
  341.  
  342. begin
  343.  
  344.   if HPtr <> Nil then
  345.     DestroyFieldList(HPtr);
  346.  
  347.   HPtr := nil;
  348.   TPtr := nil;
  349.   SPtr := nil;
  350.  
  351.   if STPtr<>nil then
  352.     DestroyStack(STPtr);
  353.  
  354.   STPtr := nil;
  355.  
  356. end; {DestroyList}
  357.  
  358. { ------------------------------------------------------------------------ }
  359.  
  360. procedure DoAdd(var MyError:boolean);
  361.  
  362. var
  363.  
  364.   A,B : real;
  365.  
  366. begin
  367.  
  368.   Pop(A,MyError);
  369.   if not MyError then begin
  370.     Pop(B,MyError);
  371.     if not MyError then Push(A+B)
  372.   end;
  373.  
  374. end; {DoAdd}
  375.  
  376. { ------------------------------------------------------------------------ }
  377.  
  378. procedure DoSub(var MyError:boolean);
  379.  
  380. var
  381.  
  382.   A,B : real;
  383.  
  384. begin
  385.  
  386.   Pop(A,MyError);
  387.   if not MyError then begin
  388.     Pop(B,MyError);
  389.     if not MyError then Push(B-A)
  390.   end;
  391.  
  392. end; {DoSub}
  393.  
  394. { ------------------------------------------------------------------------ }
  395.  
  396. procedure DoMul(var MyError:boolean);
  397.  
  398. var
  399.  
  400.   A,B : real;
  401.  
  402. begin
  403.  
  404.   Pop(A,MyError);
  405.   if not MyError then begin
  406.     Pop(B,MyError);
  407.     if not MyError then Push(A*B)
  408.   end;
  409.  
  410. end; {DoMul}
  411.  
  412. { ------------------------------------------------------------------------ }
  413.  
  414. procedure DoPI(var MyError:boolean);
  415.  
  416. begin
  417.  
  418.   MyError := false;
  419.   Push(3.1415927);
  420.  
  421. end; {DoPI}
  422.  
  423. { ------------------------------------------------------------------------ }
  424.  
  425. procedure DoABS(var MyError:boolean);
  426.  
  427. var
  428.  
  429.   A : real;
  430.  
  431. begin
  432.  
  433.   Pop(A,MyError);
  434.   if not MyError then begin
  435.     Push(abs(A))
  436.   end;
  437.  
  438. end; {DoABS}
  439.  
  440. { ------------------------------------------------------------------------ }
  441.  
  442. procedure DoATAN(var MyError:boolean);
  443.  
  444. {this function works in radians}
  445.  
  446. var
  447.  
  448.   A : real;
  449.  
  450. begin
  451.  
  452.   Pop(A,MyError);
  453.   if not MyError then begin
  454.     Push(arctan(A));
  455.   end;
  456.  
  457. end; {DoATAN}
  458.  
  459. { ------------------------------------------------------------------------ }
  460.  
  461. procedure DoCOS(var MyError:boolean);
  462.  
  463. {this function works in radians}
  464.  
  465. var
  466.  
  467.   A : real;
  468.  
  469. begin
  470.  
  471.   Pop(A,MyError);
  472.   if not MyError then begin
  473.     Push(cos(A));
  474.   end;
  475.  
  476. end; {DoCOS}
  477.  
  478. { ------------------------------------------------------------------------ }
  479.  
  480. procedure DoEXP(var MyError:boolean);
  481.  
  482. var
  483.  
  484.   A : real;
  485.  
  486. begin
  487.  
  488.   Pop(A,MyError);
  489.   if not MyError then begin
  490.     Push(exp(A));
  491.   end;
  492.  
  493. end; {DoEXP}
  494.  
  495. { ------------------------------------------------------------------------ }
  496.  
  497. procedure DoLN(var MyError:boolean);
  498.  
  499. var
  500.  
  501.   A : real;
  502.  
  503. begin
  504.  
  505.   Pop(A,MyError);
  506.   if not MyError then begin
  507.     Push(ln(A));
  508.   end;
  509.  
  510. end; {DoLN}
  511.  
  512. { ------------------------------------------------------------------------ }
  513.  
  514. procedure DoSQR(var MyError:boolean);
  515.  
  516. var
  517.  
  518.   A : real;
  519.  
  520. begin
  521.  
  522.   Pop(A,MyError);
  523.   if not MyError then begin
  524.     Push(A*A);
  525.   end;
  526.  
  527. end; {DoSQR}
  528.  
  529. { ------------------------------------------------------------------------ }
  530.  
  531. procedure DoSQRT(var MyError:boolean);
  532.  
  533. var
  534.  
  535.   A : real;
  536.  
  537. begin
  538.  
  539.   Pop(A,MyError);
  540.   if not MyError then begin
  541.     Push(sqrt(A));
  542.   end;
  543.  
  544. end; {DoSQRT}
  545.  
  546. { ------------------------------------------------------------------------ }
  547.  
  548. procedure DoDiv(var MyError:boolean);
  549.  
  550. var
  551.  
  552.   A,B : real;
  553.  
  554. begin
  555.  
  556.   Pop(A,MyError);
  557.   if not MyError then begin
  558.     Pop(B,MyError);
  559.     if not MyError then Push(B/A)
  560.   end;
  561.  
  562. end; {DoDiv}
  563.  
  564. { ------------------------------------------------------------------------ }
  565.  
  566. procedure Calculate(MyFormula:string;var MyResult:real;var MyError:boolean);
  567.  
  568. const
  569.  
  570.   NumFunctions = 12;
  571.   MyFunctions : array[1..NumFunctions] of string = ('+',
  572.                                                     '-',
  573.                                                     '*',
  574.                                                     '/',
  575.                                                     'PI',
  576.                                                     'ABS',
  577.                                                     'ARCTAN',
  578.                                                     'COS',
  579.                                                     'EXP',
  580.                                                     'LN',
  581.                                                     'SQR',
  582.                                                     'SQRT');
  583.  
  584. var
  585.  
  586.   Index,
  587.   TokenID,
  588.   TokenNum,
  589.   NumTokens : byte;
  590.   CmdTail   : ^Str128;
  591.   Token     : string;
  592.   ValError  : integer;
  593.   ValReal   : real;
  594.   VarStr    : Str20;
  595.  
  596. begin
  597.  
  598.   {set up error condition}
  599.   MyError := false;
  600.   MyResult := 0;
  601.  
  602.   NumTokens := __ParamCount(MyFormula);
  603.  
  604.   if NumTokens>0 then begin
  605.  
  606.     TokenNum := 1;
  607.     while (TokenNum<=NumTokens) AND (not MyError) do begin
  608.  
  609.       Token := __ParamStr(TokenNum,MyFormula);
  610.  
  611.       val(Token,ValReal,ValError);
  612.  
  613.       if ValError=0 then begin
  614.  
  615.         {token is a valid number - push onto stack}
  616.         Push(ValReal);
  617.  
  618.       end
  619.       else begin
  620.  
  621.         {token wasn't a number, is it an operator?}
  622.  
  623.         {convert to all caps}
  624.         for Index := 1 to length(Token) do
  625.           Token[Index] := upcase(Token[Index]);
  626.  
  627.         {search valid functions}
  628.         TokenID := 0;
  629.         for Index := 1 to NumFunctions do
  630.           if MyFunctions[Index]=Token then TokenID := Index;
  631.  
  632.         case TokenID of
  633.           0: begin
  634.                {search valid variables for Token}
  635.                VarStr := copy(Token,1,20);
  636.                ReadVariable(VarStr,ValReal,MyError);
  637.                if not MyError then
  638.                  {push variable's value onto stack}
  639.                  Push(ValReal);
  640.              end; {0}
  641.           1: DoAdd(MyError);
  642.           2: DoSub(MyError);
  643.           3: DoMul(MyError);
  644.           4: DoDiv(MyError);
  645.           5: DoPI(MyError);
  646.           6: DoABS(MyError);
  647.           7: DoATAN(MyError);
  648.           8: DoCOS(MyError);
  649.           9: DoEXP(MyError);
  650.          10: DoLN(MyError);
  651.          11: DoSQR(MyError);
  652.          12: DoSQRT(MyError);
  653.         end; {case}
  654.  
  655.       end; {if-else}
  656.  
  657.       {point to next token}
  658.       inc(TokenNum);
  659.  
  660.     end; {while}
  661.  
  662.   end
  663.   else
  664.     MyError := true;
  665.  
  666.   if not MyError then
  667.     {the result of the evaluator is on the stack}
  668.     Pop(MyResult,MyError)
  669.   else
  670.     {problem -- destroy stack}
  671.     if STPtr<>nil then DestroyStack(STPtr);
  672.  
  673. end; {Calculate}
  674.  
  675. { ------------------------------------------------------------------------ }
  676.  
  677. procedure CalcAndStore(MyFormula:string;StoreID:str20;var MyError:boolean);
  678.  
  679. var
  680.  
  681.   MyResult : real;
  682.  
  683. begin
  684.  
  685.   {call calculate to evaluate the expression}
  686.   Calculate(MyFormula,MyResult,MyError);
  687.  
  688.   {store the result in a variable}
  689.   if not MyError then
  690.     StoreVariable(StoreID,MyResult);
  691.  
  692. end; {Calculate}
  693.  
  694. { ------------------------------------------------------------------------ }
  695.  
  696. begin {init block}
  697.  
  698.   {set up linked list to empty state}
  699.  
  700.   HPtr := nil;
  701.   TPtr := nil;
  702.   SPtr := nil;
  703.  
  704.   {set up the stack}
  705.  
  706.   STPtr := nil;
  707.  
  708. end. {unit PostFix}
  709.  
  710.